package be.groept.ace.cursusplanning.model;

import java.util.*;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import be.groept.ace.cursusplanning.persistency.IPersistentieController;
import be.groept.ace.cursusplanning.persistency.IPersistentieProvider;
import be.groept.ace.cursusplanning.persistency.Persisteerbaar;
import be.groept.ace.cursusplanning.utils.observer.EventArgs;
import be.groept.ace.cursusplanning.utils.observer.PropertyChanged;
import be.groept.ace.cursusplanning.utils.observer.Subject;
import be.groept.ace.cursusplanning.utils.observer.Subscriber;

/**
 * abstracte klasse 
 * @author Gouke
 *
 */
public abstract class Cursist implements Comparable<Cursist>, Persisteerbaar, IElementVanUurroosterOverzicht{

	private String cursistNummer;
	private String naamCursist;
	private List<CursusHistoriek> cursushistorieken;
	private List<Inschrijving> inschrijvingen;
	
	private Subject<List<CursusHistoriek>> cursushistoriekenSubject;
	private Subject<List<Inschrijving>> inschrijvingenSubject;
	
	private IPersistentieProvider pProvider;
	/**
	 * Stelt een nieuwe cursist in
	 * @param cursistNummer
	 * 			nummer van de cursist
	 * @param naam
	 * 			naam van de cursist
	 * @throws IllegalArgumentException
	 */
	public Cursist(String cursistNummer, String naam) throws IllegalArgumentException{
		cursushistorieken = new ArrayList<CursusHistoriek>();
		inschrijvingen = new ArrayList<Inschrijving>();
		setCursistNummer(cursistNummer);
		setCursistNaam(naam);
		
		cursushistoriekenSubject = new PropertyChanged<List<CursusHistoriek>>();
		inschrijvingenSubject = new PropertyChanged<List<Inschrijving>>();
	}

	/**
	 * geeft cursist nummer terug
	 * @return String cursistNummer
	 */
	public String getCursistNummer() {
		return cursistNummer;
	}
	
	private void setCursistNummer(String cursistNummer) throws IllegalArgumentException{
		if (cursistNummer == null) throw new IllegalArgumentException("Cursist nummer mag niet null zijn.");
		if (cursistNummer.trim().length() == 0) throw new IllegalArgumentException("Ongeldig cursistnummer.");
		this.cursistNummer = cursistNummer.trim();
	}
	
	/**
	 * geeft naam van cursist terug
	 * @return String cursistnaam
	 */
	public String getCursistNaam(){
		return this.naamCursist;
	}
	
	private void setCursistNaam(String naam) throws IllegalArgumentException {
		if (naam == null) throw new IllegalArgumentException("Cursist nummer mag niet null zijn.");
		if (naam.trim().length() == 0) throw new IllegalArgumentException("Ongeldige naam voor de cursist.");
		this.naamCursist = naam.trim();
	}
	
	/**
	 * geeft alle cursus historieken terug
	 * @return ArrayList<CursusHistoriek> cursushiestorieken
	 */
	public List<CursusHistoriek> getCursushistorieken(){
		return this.cursushistorieken;
	}
	
	/**
	 * Geeft de cursushistoriek terug voor de opgegeven cursus en schooljaar (er kan maar 1 zijn)
	 * @param cursus
	 * @param schooljaar
	 * @return cursushistoriek of null als er geen zijn voor de opegegeven paramaters
	 */
	public CursusHistoriek getCursusHistoriek(Cursus cursus, String schooljaar) {
		for(Iterator<CursusHistoriek> i = cursushistorieken.iterator(); i.hasNext();) {
			CursusHistoriek cursusHistoriek = i.next();
			if(cursusHistoriek.getSchooljaar().equals(schooljaar) && cursusHistoriek.getCursus().equals(cursus)) 
				return cursusHistoriek;
		}
		return null;
	}
	
	/**
	 * Geeft alle cursushistorieken voor de opgegeven schooljaar
	 * @param schooljaar
	 * @return lijst van cursushistorieken
	 */
	public List<CursusHistoriek> getCursusHistorieken(String schooljaar){
		
		List<CursusHistoriek> cursusHistorieken = new ArrayList<CursusHistoriek>();
		
		for(Iterator<CursusHistoriek> i = cursushistorieken.iterator(); i.hasNext();) {
			CursusHistoriek cursusHistoriek = i.next();
			if(cursusHistoriek.getSchooljaar().equals(schooljaar)) cursusHistorieken.add(cursusHistoriek);
		}
		
		return cursusHistorieken;
	}
	
	/**
	 * voegt een cursushistoriek toe aan de interne verzameling cursushiestorieken
	 * @param historiek
	 * 		een CursusHistoriek
	 */
	public void addCursusHistoriek (CursusHistoriek historiek) throws IllegalArgumentException{
		
		if (historiek == null) throw new IllegalArgumentException("cursus historiek mag niet null zijn.");
		
		if (cursushistorieken.contains(historiek)) throw new IllegalArgumentException("Cursushistoriek bestaat al");
		
		this.cursushistorieken.add(historiek);
		
		//subscribers verwittigen van de verandering
		cursushistoriekenSubject.notifySubscribers(new EventArgs<List<CursusHistoriek>>(this.cursushistorieken));
	}
	
	/**
	 * Voegt een inschrijving toe
	 * @param inschrijving
	 * @throws IllegalArgumentException
	 */
	public void addInschrijving(Inschrijving inschrijving) throws IllegalArgumentException{
		if(inschrijving == null) throw new IllegalArgumentException("inschrijving mag niet null zijn.");
		if(getInschrijvingen().contains(inschrijving)) throw new IllegalArgumentException("Inschrijving bestaat al.");
		inschrijvingen.add(inschrijving);
		
		//subscribers verwittigen
		inschrijvingenSubject.notifySubscribers(new EventArgs<List<Inschrijving>>(this.inschrijvingen));
	}
	
	public void removeInschrijving(Inschrijving inschrijving) throws IllegalArgumentException {
		if(inschrijving == null) throw new IllegalArgumentException("inschrijving mag niet null zijn.");
		if(!getInschrijvingen().contains(inschrijving)) throw new IllegalArgumentException("Inschrijving bestaat niet bij Cursist.");
		inschrijvingen.remove(inschrijving);
	}
	
	/**
	 * Geeft alle inschrijvingen terug waarvoor de cursist ingeschreven is
	 * @return List<Inschrijving>
	 */
	public List<Inschrijving> getInschrijvingen() {
		return inschrijvingen;
	}
	
	public Opleiding getOpleiding() {
		if(getInschrijvingen().size() > 0) {
			Inschrijving eenInschrijving = getInschrijvingen().get(0);
			AangebodenCursus aangebodenCursus = eenInschrijving.getAangebodenCursus();
			return aangebodenCursus.getCursus().getOpleiding();
		}
		return null;
	}
	
	/**
	 * geeft aan of een cursist geslaagd is voor een bepaalde cursus in dit of een voorgaand schooljaar
	 * @param cursus
	 * @return True indien geslaagd
	 */
	public boolean isGeslaagdVoorCursus(Cursus cursus){
		for (CursusHistoriek ch: this.cursushistorieken){
			if (ch.getCursus() == cursus && ch.isVoldoende()){
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * geeft aan of een cursist eventuele noodzakelijk voorgaande cursussen heeft volbracht
	 * @param cursus
	 * @return TRUE indien de cursist voldoet aan de voorwaarden
	 */
	public boolean magDeelnemenCursus(Cursus cursus){
		
		Map<Integer, Cursus> voorgaandeCursussen = cursus.getVoorgaandeCursussen();
		
		Set<Integer> CursusKeys = voorgaandeCursussen.keySet();
		
		// loop 1
		/*for (int i = 0; i < voorgaandeCursussen.size(); i++){*/
			for (int key : CursusKeys){
				
				Cursus voorgaandeCursus = voorgaandeCursussen.get(key);
				
				for (int j = 0 ; j < this.cursushistorieken.size(); j++){
					if (voorgaandeCursus.equals(this.cursushistorieken.get(j).getCursus())
							&&
							this.cursushistorieken.get(j).isVoldoende()){
						// EXIT LOOP 2
						break;
					}
					else{
						if (j == this.cursushistorieken.size() - 1){
							// laatste item van cursushistorieken en geen match
							return false;
					}
				}
			}
		}
		return true;
	}
	
	/**
	 * geeft het totaal aantal studiepunten van de cursussen waarvoor de cursist zich heeft ingeschreven
	 * @param schooljaar
	 * @return int aantal studiepunten
	 */
	public int getTotaalAantalStudiePuntenVanJaar(String schooljaar){
		int totaal = 0;
		
		int jaar = Integer.parseInt(schooljaar.substring(0, 4));
		
		for (Inschrijving inschrijving : inschrijvingen){
			if (inschrijving.getEersteJaarVanSchooljaar() == jaar)
			totaal += inschrijving.getAangebodenCursus().getCursus().getStudiePunten();
		}
		
		return totaal;
	}
	
	/**
	 * geeft het aantal jaren dat een cursist was ingeschreven gedurende een aangegeven termijn
	 * @param termijn_in_jaren_vanaf_huidige_schooljaar
	 * @return int aantal jaren
	 */
	public int getAantalJarenIngeschreven( int termijn_in_jaren_vanaf_huidige_schooljaar){		
		int jaartalBegin;
		int jaartalEinde = Calendar.getInstance().get(Calendar.YEAR);
		int counterAantalJaren = 0;
		/*int maand = GregorianCalendar.MONTH;
		
		//  bepaling van het schooljaar t.o.v. het huidige jaartal. 
		
		if ( maand < 8 ){
			jaartalEinde = GregorianCalendar.YEAR - 1;
		}
		else{
			jaartalEinde = GregorianCalendar.YEAR;
		}*/
		
		jaartalBegin = jaartalEinde - termijn_in_jaren_vanaf_huidige_schooljaar;
		System.out.println("B" + jaartalBegin);
		System.out.println("I" + inschrijvingen.size());
		System.out.println("YY" + Calendar.getInstance().get(Calendar.YEAR));
		// historieken tellen
		for (int i = jaartalBegin; i <= jaartalEinde; i++){
			for (int j = 0; j < this.inschrijvingen.size(); j++){
				if (inschrijvingen.get(j).getEersteJaarVanSchooljaar() == i){
					counterAantalJaren++;
					break;  // EXIT LOOP
				}
			}
		}
		
		return counterAantalJaren;
	}
	

	/**
	 * Geeft aan of een cursist geslaagd is voor een bepaalde cursus in een bepaald jaar
	 * @param cursus
	 * @param eerste_jaar_van_schooljaar
	 * @return TRUE indien geslaagd
	 */
	public boolean wasGeslaagdTijdensSchooljaar(Cursus cursus, int eerste_jaar_van_schooljaar){
		
		for (CursusHistoriek ch : this.cursushistorieken){
			if (ch.getCursus() == cursus && ch.getEersteJaarVanSchooljaar() == eerste_jaar_van_schooljaar){
				return ch.isVoldoende();
			}
		}
		return false; //geen hit
	}

	/**
	 * geeft het type cursist als string
	 * @return String Type cursist
	 */
	public abstract String getTypeCursist();

	
	
	/**
	 * geeft de naam van de cursist
	 * @return String naamCursist
	 */
	@Override
	public String toString() {
		return this.naamCursist;
	}
	
	
	
	/**
	 * positief indien het nummer van deze cursist vroeger komt in het alfabet
	 * @param otherCursist
	 * 		een cursist
	 * @return positief indien het nummer van deze cursist vroeger komt in het alfabet
	 */
	@Override
	public int compareTo(Cursist otherCursist) {
		return this.getCursistNummer().compareTo(otherCursist.getCursistNummer());
		
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((cursistNummer == null) ? 0 : cursistNummer.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cursist other = (Cursist) obj;
		if (cursistNummer == null) {
			if (other.cursistNummer != null)
				return false;
		} else if (!cursistNummer.equals(other.cursistNummer))
			return false;
		return true;
	}
	
	@Override
	public Object[] getInfoToBeSaved(IPersistentieController pController){
		return pProvider.getInfoToBeSaved(pController);
	}

	public IPersistentieProvider getPersistentieProvider(){
		return pProvider;
	}
	public void setPersistentieProvider(IPersistentieProvider provider){
		this.pProvider = provider;
	}

	public void addCursusHistoriekenPropertyChangedSubscriber(Subscriber<List<CursusHistoriek>> subscriber) {
		cursushistoriekenSubject.addSubscriber(subscriber);
	}
	
	public void addInscrhijvingenPropertChangedSubscriber(Subscriber<List<Inschrijving>> subscriber) {
		inschrijvingenSubject.addSubscriber(subscriber);
	}
}
